using System;
using NUnit.Framework;

using ValidationFramework.Specifications;

namespace ValidationFramework.Specifications.Tests
{
	public class SatisfiesDelegateSpecTests
    {
        const string expectedRuleInterpretation = "expectedRuleInterpretation";

		[TestFixture]
		public class ResolveValidateMethod
		{
			[Test]
			public void Should_Resolve_Delegate_From_Type_And_Method_Name()
			{
				const string methodName = "ValidateValid";

                var method = SatisfiesDelegateSpec.ResolveValidateMethod(typeof(ValidationClass), methodName);

				Assert.AreEqual(typeof(ValidationClass).GetMethod(methodName), method.Method);
			}

			[Test]
			[ExpectedException(typeof(ArgumentNullException), ExpectedMessage = "Value cannot be null.\r\nParameter name: validationType")]
			public void Should_Throw_When_Given_Null_Type_Param()
			{
				Type type = null;
				var method = SatisfiesDelegateSpec.ResolveValidateMethod(type, "ValidateValid");
			}

			[Test]
			[ExpectedException(typeof(ArgumentNullException), ExpectedMessage = "Value cannot be null.\r\nParameter name: methodName")]
			public void Should_Throw_When_Given_Null_Method_Name_Param()
			{
				SatisfiesDelegateSpec.ResolveValidateMethod(typeof(ValidationClass), null);
			}

			[Test]
			[ExpectedException(typeof(ArgumentException), ExpectedMessage = "String cannot be empty.\r\nParameter name: methodName")]
			public void Should_Throw_When_Given_Empty_Method_Name_Param()
			{
				SatisfiesDelegateSpec.ResolveValidateMethod(typeof(ValidationClass), string.Empty);
			}
	
			[Test]
			[ExpectedException(typeof(ArgumentException), ExpectedMessage = "Could not load the method 'NonExistantMethod' from type 'ValidationFramework.Specifications.Tests.SatisfiesDelegateSpecTests+ValidationClass'.\r\nParameter name: validationMethod")]
			public void Should_Throw_When_It_Cannot_Resolve_The_Method()
			{
				// ValidateValid2 doesn't exist
				SatisfiesDelegateSpec.ResolveValidateMethod(typeof(ValidationClass), "NonExistantMethod");
			}
		}

		[TestFixture]
		public class Contructor
		{
			[Test]
			public void Should_Use_Supplied_Delegate()
			{
				var validator = new SatisfiesDelegateSpec(ValidationClass.ValidateValid, expectedRuleInterpretation);
				Assert.AreEqual(typeof(ValidationClass).GetMethod("ValidateValid"), validator._specDelegate.Method);
			}

			[Test]
			[ExpectedException(typeof(ArgumentNullException), ExpectedMessage = "Value cannot be null.\r\nParameter name: ruleInterpretation")]
			public void Should_Throw_When_Given_Null_Rule_Interpretation()
			{
				new SatisfiesDelegateSpec(ValidationClass.ValidateValid, null);
			}

			[Test]
			[ExpectedException(typeof(ArgumentException), ExpectedMessage = "String cannot be empty.\r\nParameter name: ruleInterpretation")]
			public void Should_Throw_When_Given_Empty_Rule_Interpretation()
			{
				new SatisfiesDelegateSpec(ValidationClass.ValidateValid, string.Empty);
			}
		}

		[TestFixture]
		public class IsEquivalentTo
		{

			[Test]
			public void Should_Be_Equivalent_When_Constructed_With_Matching_Parameters()
			{
				var validator1 = new SatisfiesDelegateSpec(ValidationClass.ValidateValid, expectedRuleInterpretation);
				var validator2 = new SatisfiesDelegateSpec(ValidationClass.ValidateValid, expectedRuleInterpretation);
				Assert.IsTrue(validator1.IsEquivalentTo(validator2));
			}

			[Test]
			public void Should_NOT_Be_Equivalent_With_Different_ValidationMethods()
			{
				var validator1 = new SatisfiesDelegateSpec(ValidationClass.ValidateValid, expectedRuleInterpretation);
				var validator2 = new SatisfiesDelegateSpec(ValidationClass.ValidateInValid, expectedRuleInterpretation);
				Assert.IsFalse(validator1.IsEquivalentTo(validator2));
			}

			[Test]
			public void Should_NOT_Be_Equivalent_With_Matching_ValidationMethod_Name_On_Different_Class()
			{
				var validator1 = new SatisfiesDelegateSpec(ValidationClass.ValidateValid, expectedRuleInterpretation);
				var validator2 = new SatisfiesDelegateSpec(ValidationClass2.ValidateValid, expectedRuleInterpretation);
				Assert.IsFalse(validator1.IsEquivalentTo(validator2));
			}

		}

		[TestFixture]
		public class Validate
		{

			[Test]
			public void Should_Use_Supplied_Delegate_To_Test_Values()
			{
				var validator1 = new SatisfiesDelegateSpec(ValidationClass.ValidateValid, expectedRuleInterpretation);
				var validator2 = new SatisfiesDelegateSpec(ValidationClass.ValidateInValid, expectedRuleInterpretation);

				Assert.AreEqual(validator1.IsSatisfiedBy(null), ValidationClass.ValidateValid(null));
				Assert.AreEqual(validator2.IsSatisfiedBy(null), ValidationClass.ValidateInValid(null));
			}

		}

		// this might not be relevant anymore
		//[TestFixture]
		//public class GetComputedErrorMessage
		//{
		//    [Test]
		//    public void Simple()
		//    {
		//        var rule = new DelegateSpec(typeof(ValidationClass), "ValidateInValidWithErrorMessage", expectedRuleInterpretation);
		//        rule.SetDefaultErrorMessage(new MockInfoDescriptor(typeof(object).TypeHandle, "foo"));
		//        Assert.IsFalse(string.IsNullOrEmpty(rule.ErrorMessage));
		//    }
		//}


        #region class Foo (for testing)

        public static class ValidationClass
        {


			public static bool ValidateValid(object target)
            {
				return true;
            }


			public static bool ValidateInValid(object target)
            {
				return false;
            }
        }


        public class ValidationClass2
        {

			public static bool ValidateValid(object target)
			{
				return true;
			}

			public static bool ValidateInValid(object target)
			{
				return false;
			}
        }

        #endregion
    }
}